package com.hfut.wxy.common.config.swagger;

import com.alibaba.fastjson.JSONArray;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ResolvedMethodParameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.DocumentationContext;
import springfox.documentation.spi.service.contexts.ParameterContext;

@Component
@Order   //plugin加载顺序，默认是最后加载
public class MapApiReader implements ParameterBuilderPlugin {
    @Autowired
    private TypeResolver typeResolver;

    @Override
    public void apply(ParameterContext parameterContext) {
        ResolvedMethodParameter methodParameter = parameterContext.resolvedMethodParameter();
        if (methodParameter.getParameterType().canCreateSubtype(JSONArray.class)) {//判断参数类型
            Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class);  //根据参数上的ApiJsonObject注解中的参数动态生成Class
            if (optional.isPresent()) {//若存在@ApiJsonObject
                String modelName = optional.get().name();
                ApiJsonProperty[] fields = optional.get().value();
                Class newClass = createRefModel(fields, modelName);
                DocumentationContext documentationContext = parameterContext.getDocumentationContext();
                documentationContext.getAdditionalModels().add(typeResolver.resolve(newClass));  //向documentContext的Models中添加我们新生成的Class
                parameterContext.parameterBuilder()
                        .parameterType("body")
                        .modelRef(new ModelRef(modelName))//修改ModelRef为我们动态生成的model
                        .name(modelName);
            }
        }else {
            Optional<ApiJsonObject> optional = methodParameter.findAnnotation(ApiJsonObject.class);  //根据参数上的ApiJsonObject注解中的参数动态生成Class
            if (optional.isPresent()) {//若存在@ApiJsonObject
                String modelName = optional.get().name();
                ApiJsonProperty[] fields = optional.get().value();
                Class newClass = createRefModel(fields, modelName);
                DocumentationContext documentationContext = parameterContext.getDocumentationContext();
                documentationContext.getAdditionalModels().add(typeResolver.resolve(newClass));  //向documentContext的Models中添加我们新生成的Class
                parameterContext.parameterBuilder()
                        .parameterType("body")
                        .modelRef(new ModelRef(modelName))//修改ModelRef为我们动态生成的model
                        .name(modelName);
            }
        }
    }

    private final static String basePackage = "com.ahaxt.ptp.entity.swagger_model.";

    /**
     * 动态生成含有Swagger注解的javaBeen
     */
    private Class createRefModel(ApiJsonProperty[] propertys, String name) {
        try {
            CtClass ctClass = ClassPool.getDefault().makeClass(basePackage + name);
            for (ApiJsonProperty property : propertys) {
                ctClass.addField(createField(property, ctClass));
            }
            return ctClass.toClass();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据property的值生成含有ApiModelProperty注解的CtField
     */
    private CtField createField(ApiJsonProperty property, CtClass ctClass) throws NotFoundException, CannotCompileException {
        CtField ctField = new CtField(ClassPool.getDefault().get(property.dataType().getName()), property.name(), ctClass);
        ctField.setModifiers(Modifier.PUBLIC);

        ConstPool constPool = ctClass.getClassFile().getConstPool();
        AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);

        Annotation ann = new Annotation("io.swagger.annotations.ApiModelProperty", constPool);
        ann.addMemberValue("value", new StringMemberValue(property.value(), constPool));
        ann.addMemberValue("required",new BooleanMemberValue(property.required(),constPool));
        ann.addMemberValue("example", new StringMemberValue(property.example(), constPool));
        ann.addMemberValue("hidden", new BooleanMemberValue(property.hidden(), constPool));

        attr.addAnnotation(ann);
        ctField.getFieldInfo().addAttribute(attr);

        return ctField;
    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}
